using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.ComponentModel;

namespace GoodStuff.Web.Controls
{
	/// <summary>
	/// This user control represents an image that only a real human should be
	/// able to decode. It works in conjunction with the CaptchaImageHandler
	/// and the CaptchaValidator controls. This control does nothing more than
	/// generate the ticket for validation, and delegate the creation of an image
	/// to the handler.
	/// </summary>
	/// <remarks>
	/// The ticket with the correct answer is to be encrypted in the viewstate.
	/// 
	/// Implementation of this class is inspired by an MSDN article at
	/// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnaspp/html/hip_aspnet.asp?frame=true
	/// </remarks>
	[DefaultProperty("CaptchaImageHandler")]
	public class CaptchaImage : System.Web.UI.WebControls.WebControl
	{
		protected override System.Web.UI.HtmlTextWriterTag TagKey
		{
			get
			{
				return HtmlTextWriterTag.Img;
			}
		}

		/// <summary>
		/// The name of the URL that will generate the image. By default, this
		/// property contains 'CaptchaImageHandler.axd', but implementors may
		/// decide to override this.
		/// </summary>
		[DefaultValue("CaptchaImageHandler.axd")]
		public string CaptchaImageHandler
		{
			get
			{
				string o = ViewState["CaptchaImageHandler"] as string;
				if(o != null)
					return o;
				return "CaptchaImageHandler.axd";
			}
			set
			{
				ViewState["CaptchaImageHandler"] = value;
			}
		}

		[Browsable(false)]
		private Guid Key
		{
			get
			{
				object o = ViewState["SecretID"];
				if(o is Guid)
					return (Guid)o;

				return Guid.Empty;
			}
			set
			{
				ViewState["SecretID"] = value;
			}
		}

		protected override void OnPreRender(EventArgs e)
		{
			base.OnPreRender (e);

			//generate a unique key for storage in application cache to transmit the
			//chosen password to the image AND to remember it for validation.

			//the good stuff is that this key does survive a postback long enough
			//to survive the control validation phase. After that, a new image
			//is generated with new key.

			//Make sure that this token is not stored in viewstate unencrypted.
			string token = Security.SecurePassword.CreateRandomPassword(6);
			Key = Guid.NewGuid();

			SetToken(Key.ToString("N"), token, TimeSpan.FromMinutes(1));
		}

		protected override void Render(HtmlTextWriter writer)
		{
			string imgUrl = string.Format("{0}?key={1}&width={2}&height={3}", CaptchaImageHandler, Key.ToString("N"), (int)this.Width.Value, (int)this.Height.Value);
			
			writer.AddAttribute("src", imgUrl, false);
			base.Render (writer);
		}


		/// <summary>
		/// Check if the given token correspondents to the token of this image.
		/// </summary>
		/// <param name="password"></param>
		/// <returns></returns>
		internal bool Authenticate(string password)
		{
			//match the token stored in application cache with the given token.
			if(password == GetToken(Key.ToString("N")))
				return true;

			return false;
		}

		internal static void SetToken(string id, string token, TimeSpan timeout)
		{
			System.Web.HttpContext.Current.Cache.Add("__captchaCache" + id, token, null, DateTime.MaxValue, timeout, System.Web.Caching.CacheItemPriority.Normal, null);
		}

		internal static string GetToken(string id)
		{
			string storedToken = System.Web.HttpContext.Current.Cache["__captchaCache" + id] as string;

			return storedToken;
		}
	}
}
